package com.study.service;

import com.study.controller.Ranking;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author : wilbur.yuan
 * @date : 2018-11-20
 */
@Service
public class RankingService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisScript<String> redisScript;


    public String addRanking(Long msgId, String albumId, String[] args) {

        return redisTemplate.execute(redisScript, getRedisKeys(msgId, albumId), args);
    }


    public Integer querySum(String albumId) {
        String str = redisTemplate.opsForValue().get(getSumKey(albumId));
        return Optional.ofNullable(str).map(Integer::valueOf).orElse(0);
    }

    public List<Ranking> queryRankings(String albumId, long limit) {

        Set<ZSetOperations.TypedTuple<String>> rankingSet =
                redisTemplate.opsForZSet().reverseRangeWithScores(getZsetKey(albumId), 0, limit - 1);

        return rankingSet.stream()
                .map(o -> {
                    Ranking ranking = new Ranking();
                    ranking.setUid(Long.valueOf(o.getValue()));
                    ranking.setQuantity(extractQuantity(o.getScore()));
                    return ranking;
                }).collect(Collectors.toList());
    }

    public Ranking queryRanking(Long userId, String albumId) {
        Ranking ranking = new Ranking();
        ranking.setUid(userId);

        Long rank = redisTemplate.opsForZSet().reverseRank(getZsetKey(albumId), String.valueOf(userId));
        ranking.setRank(rank == null ? Integer.MAX_VALUE : rank + 1);

        Double score = redisTemplate.opsForZSet().score(getZsetKey(albumId), String.valueOf(userId));
        ranking.setQuantity(extractQuantity(score));

        Set<ZSetOperations.TypedTuple<String>> set =
                redisTemplate.opsForZSet().reverseRangeWithScores(getZsetKey(albumId), 29, 29);

        int onBoardQuantity = set.stream().mapToInt(o -> extractQuantity(o.getScore()) + 1).findAny().orElse(1);

        int behindQuantity = onBoardQuantity - ranking.getQuantity();

        ranking.setBehindQuantity(behindQuantity > 0 ? behindQuantity : 0);

        return ranking;
    }


    private int extractQuantity(Double oldScore) {
        if (oldScore == null)
            return 0;
        String strScore = String.valueOf(oldScore.longValue());
        String strQuantity = strScore.substring(0, strScore.length() - 10);
        return Integer.valueOf(strQuantity);
    }

    private List<String> getRedisKeys(Long msgId, String albumId) {
        List<String> keys = new ArrayList<>(3);

        keys.add(getOrderKey(msgId));
        keys.add(getSumKey(albumId));
        keys.add(getZsetKey(albumId));

        return keys;
    }

    private String getOrderKey(Long msgId) {
        return String.format("rankings:order:%s", msgId);
    }

    private String getSumKey(String albumId) {
        return String.format("rankings:sum:%s", albumId);
    }

    private String getZsetKey(String albumId) {
        return String.format("rankings:idv:%s", albumId);
    }
}
